namespace SimpleWebServices.Models
{
    using System;
    using System.Collections.Specialized;
    using System.Web;
    using System.Web.Mvc;
    using Moq;
    using SimpleWebServices;
    using SimpleWebServices.Configuration;
    using SimpleWebServices.Data;
    using SimpleWebServices.Web;
    using SimpleWebServices.Web.UI.Html;
    using SimpleWebServices.Web.UI.Html.Elements;
    using Xunit;

    public sealed class ModelBinderFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<ModelBinder<IContainForm>>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .Implements<IModelBinder>()
                .Result);
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new ModelBinder<TestFormContainer>());
        }

        [Fact]
        public void op_BindModel_ControllerContextTokenMissing_ModelBindingContext()
        {
            Default.Clear();
            FakeRepository.Clear();

            try
            {
                IRepository sessions = FakeRepository.Register<FormSession>();

                NameValueCollection form = new NameValueCollection();
                form.Add("control", "value");

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/contact").Verifiable();
                HttpContextFactory.Mock = mock.Object;

                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='/test'>" +
                            "<label id='error'/>" +
                            "<input type='text' id='control' name='control'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                var controller = new Mock<ControllerContext>();
                controller.SetupGet(x => x.HttpContext.Request.Form).Returns(form).Verifiable();

                IContainForm obj = new ModelBinder<TestFormContainer>().BindModel(controller.Object, new Mock<ModelBindingContext>().Object) as IContainForm;

                Assert.Equal<string>("value", sessions.Select<FormSession>(obj.Token.Value).Data["control"]);
                Assert.Equal<string>("value", (obj.Form["control"] as TextInput).Value);

                mock.VerifyAll();
                controller.VerifyAll();
            }
            finally
            {
                Default.Reset();
                FakeRepository.Reset();
                HttpContextFactory.Reset();
            }
        }

        [Fact]
        public void op_BindModel_ControllerContext_ModelBindingContext()
        {
            Default.Clear();
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                IRepository sessions = FakeRepository.Register<FormSession>(new
                {
                    token = token,
                    xml = "<session><data><value name='control'>value</value></data></session>"
                });

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/contact?" + token).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='/test'>" +
                            "<label id='error'/>" +
                            "<input type='text' id='control' name='control'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                NameValueCollection form = new NameValueCollection();
                form.Add("control", "updated");

                var controller = new Mock<ControllerContext>();
                controller.SetupGet(x => x.HttpContext.Request.Form).Returns(form).Verifiable();

                IContainForm obj = new ModelBinder<TestFormContainer>().BindModel(controller.Object, new Mock<ModelBindingContext>().Object) as IContainForm;

                Assert.Equal<string>("updated", sessions.Select<FormSession>(obj.Token.Value).Data["control"]);
                Assert.Equal<string>("updated", (obj.Form["control"] as TextInput).Value);

                mock.VerifyAll();
                controller.VerifyAll();
            }
            finally
            {
                Default.Reset();
                FakeRepository.Reset();
                HttpContextFactory.Reset();
            }
        }

        [Fact]
        public void op_BindModel_ControllerContextNull_ModelBindingContext()
        {
            Assert.Throws<ArgumentNullException>(() => new ModelBinder<TestFormContainer>().BindModel(null as ControllerContext, new Mock<ModelBindingContext>().Object));
        }
    }
}